package hn.sigit.logic.bpm;


import hn.sigit.logic.ladmshadow.ShadowToLADM;
import hn.sigit.logic.ladmshadow.Util;
import hn.sigit.model.hnd.administrative.HND_Request;
import hn.sigit.model.hnd.administrative.HND_RequestSubType;
import hn.sigit.model.hnd.administrative.HND_RequestType;
import hn.sigit.model.hnd.administrative.dao.HND_RequestDAO;
import hn.sigit.model.ladm.external.ExtParty;
import hn.sigit.model.ladm.external.dao.ExtPartyDAO;
import hn.sigit.util.AppOptions;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.annotations.bpm.CreateProcess;
import org.jboss.seam.annotations.bpm.EndTask;
import org.jboss.seam.bpm.Actor;
import org.jboss.seam.bpm.ProcessInstance;
import org.jboss.seam.bpm.TaskInstance;
import org.jbpm.JbpmContext;
import org.jbpm.db.LoggingSession;
import org.jbpm.taskmgmt.log.TaskAssignLog;
import org.jbpm.taskmgmt.log.TaskEndLog;

@Name("transactionProcess")
@Scope(value = ScopeType.CONVERSATION)
@AutoCreate
public class TransactionProcessBean {
	//Variables for PRESENTATION activity-----------------------------
	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private Date presentationDate;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private Long partyId;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String partyNationalIdentity;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String partyName;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String cashiersCode;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private BigDecimal payment;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String description;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	List<AttachedFileData> attachedFiles;
	
	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String receptionistUserName;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String receptionistFullName;
	//END Variables for PRESENTATION activity-------------------------
	
	//Variables for Analysis activity------------------------------------
	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String analystUserName;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String analystFullName;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private HND_RequestType requestType;
	
	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private HND_RequestSubType requestSubType;
	
	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private List<ParcelData> parcelDataList;
	
	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String analysisDictum;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String additionalObservations;
	
	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private Long presentationId;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private Set<Long> shadowBAUnitIds;
	//END Variables for Analysis activity--------------------------------
	
	//Variables for DATAENTRY activity------------------------------------
	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String dataEntryUserName;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String dataEntryFullName;
	//END Variables for DATAENTRY activity--------------------------------
	
	//Variables for APPROVAL activity------------------------------------
	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String approverUserName;

	@In(scope = ScopeType.BUSINESS_PROCESS, required = false)
	@Out(scope = ScopeType.BUSINESS_PROCESS, required = false)
	private String approverFullName;
	//END Variables for APPROVAL activity--------------------------------

	@In
	private AppOptions appOptions;

	@In
	private TransactionController transactionController;

	@In
	private Actor actor;

	//TODO: loggedUserName deberia ser requerido. Corregir despues
	@In(create=true, required=false)
	private String loggedUserName;

	//Getters and setters for PRESENTATION Activity----------------------------
	public Date getPresentationDate() {
		return presentationDate;
	}
	
	public String getReceptionistUserName() {
		return receptionistUserName;
	}
	public void setReceptionistUserName(String receptionistUserName) {
		this.receptionistUserName = receptionistUserName;
	}
	
	public String getReceptionistFullName() {
		return receptionistFullName;
	}
	public void setReceptionistFullName(String receptionistFullName) {
		this.receptionistFullName = receptionistFullName;
	}
	
	public Long getPartyId() {
		return partyId;
	}
	public void setPartyId(Long partyId) {
		this.partyId = partyId;
	}
	
	public String getPartyNationalIdentity() {
		return partyNationalIdentity;
	}
	public void setPartyNationalIdentity(String partyNationalIdentity) {
		this.partyNationalIdentity = partyNationalIdentity;
	}

	public String getPartyName() {
		return partyName;
	}
	public void setPartyName(String partyName) {
		this.partyName = partyName;
	}
	
	public String getCashiersCode() {
		return cashiersCode;
	}
	public void setCashiersCode(String cashiersCode) {
		this.cashiersCode = cashiersCode;
	}
	
	public BigDecimal getPayment() {
		return payment;
	}
	public void setPayment(BigDecimal payment) {
		this.payment = payment;
	}
	
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}

	public List<AttachedFileData> getAttachedFiles() {
		return attachedFiles;
	}
	public void setAttachedFiles(List<AttachedFileData> attachedFiles) {
		this.attachedFiles = attachedFiles;
	}
	
	public Long getPresentationId() {
		return presentationId;
	}
	public void setPresentationId(Long presentationId) {
		this.presentationId = presentationId;
	}
	//END Getters and setters for PRESENTATION Activity-----------------------------


	//BEGIN getters and setters for ANALYSIS activity-------------------------------
	public String getAnalystUserName() {
		return analystUserName;
	}
	public void setAnalystUserName(String analystUserName) {
		this.analystUserName = analystUserName;
	}
	
	public String getAnalystFullName() {
		return analystFullName;
	}
	public void setAnalystFullName(String analystFullName) {
		this.analystFullName = analystFullName;
	}
	
	public HND_RequestType getRequestType() {
		return requestType;
	}
	public void setRequestType(HND_RequestType requestType) {
		this.requestType = requestType;
	}
	
	public HND_RequestSubType getRequestSubType() {
		return requestSubType;
	}
	public void setRequestSubType(HND_RequestSubType requestSubType) {
		this.requestSubType = requestSubType;
	}
	
	public List<ParcelData> getParcelDataList() {
		return parcelDataList;
	}
	public void setParcelDataList(List<ParcelData> parcelDataList) {
		this.parcelDataList = parcelDataList;
	}
	
	public String getAnalysisDictum() {
		return analysisDictum;
	}
	public void setAnalysisDictum(String analysisDictum) {
		this.analysisDictum = analysisDictum;
	}
	
	public String getAdditionalObservations() {
		return additionalObservations;
	}
	public void setAdditionalObservations(String additionalObservations) {
		this.additionalObservations = additionalObservations;
	}
	
	public Set<Long> getShadowBAUnitIds() {
		return shadowBAUnitIds;
	}
	public void setShadowBAUnitIds(Set<Long> shadowBAUnitIds) {
		this.shadowBAUnitIds = shadowBAUnitIds;
	}
	//END getters and setters for analysis activity-------------------------------
	

	//Getters and setters for DATAENTRY activity----------------------------------
	public String getDataEntryUserName() {
		return dataEntryUserName;
	}
	public void setDataEntryUserName(String dataEntryUserName) {
		this.dataEntryUserName = dataEntryUserName;
	}
	
	public String getDataEntryFullName() {
		return dataEntryFullName;
	}
	public void setDataEntryFullName(String dataEntryFullName) {
		this.dataEntryFullName = dataEntryFullName;
	}
	//END Getters and setters for DATAENTRY activity------------------------------
	
	//Getters and setters for APPROVAL activity-----------------------------------
	public String getApproverUserName() {
		return approverUserName;
	}

	public void setApproverUserName(String approverUserName) {
		this.approverUserName = approverUserName;
	}

	public String getApproverFullName() {
		return approverFullName;
	}
	public void setApproverFullName(String approverFullName) {
		this.approverFullName = approverFullName;
	}
	//END Getters and setters for APPROVAL activity-------------------------------
	
	@End
	@CreateProcess(definition="transactionWorkflow", processKey="#{presentationId}")
	@Transactional
	public String beginProcess() {
		presentationDate = new Date();

		setPresentationData();
		return "activities";
	}
	
	@EndTask
	public void endPresentation() {
		setPresentationData();
	}
	
	
	/*
	 * ANALYSIS STEP METHODS
	 */
	@EndTask
	public void endAnalysis() {
		setAnalysisData();

		shadowBAUnitIds = new HashSet<Long>();
		
		List<Long> parcelIdList = new ArrayList<Long>();
		for (ParcelData pd : parcelDataList)
			parcelIdList.add(Long.valueOf(pd.getSuID()));
			
		shadowBAUnitIds.addAll( Util.copyParcelDataWithRelatedParcels( presentationId, parcelIdList, true, true ) );
	}
	/************ END *************/
	
	
	/*
	 * DATA ENTRY STEP METHODS
	 */
	@EndTask(transition="dataentry_ok")
	public void endDataEntry() {
		setDataEntryData();
	}
	
	@EndTask(transition="dataentry_back2analysis")
	public void dataEntryBackToAnalysis() {
	}
	/************ END *************/
	
	/*
	 * APPROVAL STEP METHODS
	 */
	@EndTask(transition="approval_ok")
	public void endApproval() {
		setApprovalData();
		
		//TODO: Copy from shadow schema to front-end schema
		ShadowToLADM.applyPresentationChanges(presentationId);
	}
	
	@EndTask(transition="approval_back2analysis")
	public void approvalBackToAnalysis() {
	}
	
	@EndTask(transition="approval_back2dataentry")
	public void approvalBackToDataEntry() {
	}
	/************ END *************/
	
	private void setPresentationData() {
		HND_Request hndRequest;
		
		Long presentationId = transactionController.getPresentationId();
		if (presentationId == null)
			hndRequest = new HND_Request();
		else
			hndRequest = HND_RequestDAO.loadRequestByPresentationId(presentationId);
		if (hndRequest == null) hndRequest = new HND_Request();
		
		ExtParty extParty = ExtPartyDAO.loadExtPartyByExtPID(transactionController.getPartyId());
		
		hndRequest.setPresentationDate(presentationDate);
		hndRequest.setReceptionistFullName(loggedUserName);
		hndRequest.setReceptionistUserName(actor.getId());
		hndRequest.setExtParty(extParty);
		hndRequest.setCashiersCode(transactionController.getCashiersCode());
		hndRequest.setPayment(transactionController.getPayment());
		hndRequest.setDescription(transactionController.getDescription());
		
		HND_RequestDAO.save(hndRequest);
		
		setReceptionistFullName(loggedUserName);
		setReceptionistUserName(actor.getId());
		setPartyId(transactionController.getPartyId());
		setPartyNationalIdentity(transactionController.getPartyNationalIdentity());
		setPartyName(transactionController.getPartyName());
		setCashiersCode(transactionController.getCashiersCode());
		setPayment(transactionController.getPayment());
		setDescription(transactionController.getDescription());
		setAttachedFiles(transactionController.getAttachedFiles());
		
		setPresentationId(hndRequest.getPresentationId());
		transactionController.setPartyId(hndRequest.getPresentationId());

		if (attachedFiles != null) {
			File fIn, fOut;
			for (AttachedFileData afd : attachedFiles) {
				fIn = afd.getFile();
				if (fIn != null) {
					fOut = new File(appOptions.getUploadFileSavePath() + '/' + afd.getFileName());
					
					//TODO: DEFINIR POLITICA DE MANEJOR DE EXCEPCIONES
					try {
						copyFile(new FileInputStream(fIn), new FileOutputStream(fOut));
			
						afd.setFullPath(fOut.getCanonicalPath());
					}
					catch (FileNotFoundException fnfe) {
						FacesContext.getCurrentInstance().addMessage("oops",
								new FacesMessage("No se encontro un archivo"));
					}
					catch (IOException ioe) {
						FacesContext.getCurrentInstance().addMessage("oops",
								new FacesMessage("No se pudo adjuntar uno o mas archivos"));
					}
				}
			}
		}
	}
	
	private void setAnalysisData() {
		setAnalystFullName(loggedUserName);
		setAnalystUserName(actor.getId());
		setRequestType(transactionController.getRequestType());
		setRequestSubType(transactionController.getRequestSubType());
		setParcelDataList(transactionController.getParcelDataList());
		setAnalysisDictum(transactionController.getAnalysisDictum());
		setAdditionalObservations(transactionController.getAdditionalObservations());
	}
	
	private void setDataEntryData() {
		setDataEntryFullName(loggedUserName);
		setDataEntryUserName(actor.getId());
	}
	
	private void setApprovalData() {
		setApproverFullName(loggedUserName);
		setApproverUserName(actor.getId());
	}
	
	public void updateProcessData() {
		setPresentationData();
	}

	@SuppressWarnings("unchecked")
	public List<ItemHistorialActividad> getLog() {
		LoggingSession ls = new LoggingSession(JbpmContext
				.getCurrentJbpmContext().getSession());
		List eventList = ls.findLogsByToken(TaskInstance.instance().getToken()
				.getId());
		int cont = 0;

		List<ItemHistorialActividad> retval = new ArrayList<ItemHistorialActividad>();
		ItemHistorialActividad newItem = null;

		Iterator it = eventList.iterator();
		Object obj;
		while (it.hasNext()) {
			obj = it.next();
			if (null != obj) {
				if (0 == cont) {
					newItem = new ItemHistorialActividad();
					retval.add(newItem);
				}
				cont++;
				/*
				 * if (obj instanceof TaskCreateLog) { cont = 1;
				 * newItem.setTarea
				 * (((TaskCreateLog)obj).getTaskInstance().getName()); }
				 */
				if (obj instanceof TaskAssignLog) {
					newItem.setTarea(((TaskAssignLog) obj).getTaskInstance()
							.getName());
					newItem.setActor(((TaskAssignLog) obj).getTaskInstance()
							.getActorId());
					newItem.setFechaAsignado(((TaskAssignLog) obj).getDate());
				} else if (obj instanceof TaskEndLog) {
					cont = 0;
					newItem.setFechaFinalizado(((TaskEndLog) obj).getDate());
				}
			}
		}

		// Map m =
		// ls.findLogsByProcessInstance(ProcessInstance.instance().getId());

		return retval;
	}

	private void copyFile(FileInputStream sourceStream,
			FileOutputStream destinationStream) throws IOException {
		FileChannel inChannel = sourceStream.getChannel();
		FileChannel outChannel = destinationStream.getChannel();
		try {
			inChannel.transferTo(0, inChannel.size(), outChannel);
		} catch (IOException e) {
			throw e;
		} finally {
			if (inChannel != null) {
				try {
					inChannel.close();
				} catch (IOException ioe) {
				}
			}
			if (outChannel != null)
				outChannel.close();
		}
	}
}
